package com.lishuang.base.ThreadPoolUtil;

import com.lishuang.base.Enums.DeviceStatus;
import com.lishuang.base._Exception_.DeviceIDNullException;
import com.lishuang.base._Exception_.IllegalPriorityException;
import com.lishuang.base._Exception_.NotADeviceException;
import com.lishuang.domain.json.Device;
import com.lishuang.manager.DeviceScheduler;
import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.function.Supplier;

@SuppressWarnings("unused")
/**
 * @ClassName 与PriorityDistributedBlockingQueue配套的线程池
 * @Description
 * @Author LiShuang
 * @Date * 2022/11/8 7:31
 */
public class PriorityDistributedThreadPoolExecutor extends ThreadPoolExecutor {

    public int getMinPriority() {
        return minPriority;
    }

    public int getDefaultPriority() {
        return defaultPriority;
    }

    public int getMaxPriority() {
        return maxPriority;
    }

    private final int minPriority = 0;
    private final int defaultPriority = 5;
    private final int maxPriority = 100;
    /**
     * 线程池用完了，队列满了，任务添加到线程池的时候就会有问题，
     * 实现RejectedExecutionHandler接口，自定义拒绝策略
     */
    private volatile RejectedExecutionHandler handler;

    /**
     * 有界优先级队列
     */
    private final PriorityDistributedBlockingQueue workQueue;

    public PriorityDistributedThreadPoolExecutor(
            int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            Map<Integer, BlockingQueue<Runnable>> queueMap,
            ThreadFactory threadFactory) {
        this(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                new PriorityDistributedBlockingQueue(queueMap),
                threadFactory,
                new CallerRunsPolicy());
    }

    public PriorityDistributedThreadPoolExecutor(
            int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            PriorityDistributedBlockingQueue workQueue,
            ThreadFactory threadFactory
    ) {
        this(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                new CallerRunsPolicy());
    }

    //带拒绝策略
    public PriorityDistributedThreadPoolExecutor(
            int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            PriorityDistributedBlockingQueue workQueue,
            ThreadFactory threadFactory,
            RejectedExecutionHandler handler) {
        super(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler);
        this.handler = handler;
        this.workQueue = workQueue;
    }

    @Override
    public void execute(Runnable command) {
        if (!(command instanceof Device)) {
            //不是一个设备！
            throw new NotADeviceException("This parameter MUST BE a runnable device!");
        }
        super.execute(command);
    }

    public void execute(@NotNull Device device) throws Exception {
        if (device.getDeviceId() == 0) throw new DeviceIDNullException("DeviceID cant be empty!");
        if (device.getDevicePriority()<minPriority||device.getDevicePriority()>maxPriority) throw new IllegalPriorityException("Please Check Input Priority! It should range from 1 to 10!");

        Constructor<?> constructor = null;
        //  TODO 如果设备类所在的包路径改变，这里也必须改变！
        constructor = Class.forName("com.lishuang.deviceCategory.Impl." + device.getDeviceCategory()).getConstructor(Device.class);
        Device d = (Device) constructor.newInstance(device);
        if (super.getPoolSize() < super.getMaximumPoolSize()
                || device.getDevicePriority() <= workQueue.getMinPriority()) {
            execute((Runnable) d);
            d.setUuid(UUID.randomUUID().toString());
            DeviceScheduler.map.put(d.getUuid(),d);
            //更改设备状态为running
            d.setDeviceStatus(DeviceStatus.running);
        } else if (!workQueue.offer((Runnable) new Device(device)))
            handler.rejectedExecution((Runnable) d, this);
        //更改设备状态为abnormal
        device.setDeviceStatus(DeviceStatus.abnormal);
    }
    @Deprecated
    public void execute(int deviceID, String deviceCategory) throws Exception {
        execute(new Device(deviceID,deviceCategory));
    }



    public <U> CompletableFuture<U> execute(Supplier<U> supplier, int priority) {
        return FutureUtils.supplyAsync(supplier, this, priority);
    }
}
